export const metadata = {
  title: "DOM | Challenges",
}

### 1. Show the different ways of selecting an element from DOM

- The element can be selected using its tagname, id, attribute, value etc

```js copy
document.getElementById("elementId"); // elementId is id of the element
document.querySelector(".my-class"); // element having a class 'my-class'
```

**Notes**

Above selectors are used to select a first matching element reference even if multiple matches be there

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
- https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById

---

### 2. Show the ways to loop over the Nodelist obtained after querying for the elements

- The Nodelist can be obtained by queryng for multiple elements
- As list of elements are array like object (not pure JS array), all the array methods can't be directly used

```js copy
// Lets assume DOM elements are fetched in the variable domElements
for (let element of domElements) {
  // perform operation on element
}
```

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/NodeList

---

### 3. Design and Implement a Node Store, which supports DOM element as key

- Implement it without using inbuilt Map
- Can you do it in O(1) Time complexity?

```js copy
class NodeStore {
  constructor() {
    this.store = {};
  }
  /**
   * @param {Node} node
   * @param {any} value
   */
  set(node, value) {
    node.__nodeIdentifier__ = Symbol();
    this.store[node.__nodeIdentifier__] = value;
  }
  /**
   * @param {Node} node
   * @return {any}
   */
  get(node) {
    return this.store[node.__nodeIdentifier__];
  }

  /**
   * @param {Node} node
   * @return {Boolean}
   */
  has(node) {
    return !!this.store[node.__nodeIdentifier__];
  }
}
```

**References**

- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

---

### 4. Implement a function to find the closest ancestor with the provided selector (Element.closest() method)

- The closest() method traverses the Element and its parents (heading toward the document root) until it finds a node that matches the provided selector string. Will return itself or the matching ancestor. If no such element exists, it returns null.

```js copy
Element.prototype.closest = function (selector) {
  var el = this;
  while (el) {
    if (el.matches(selector)) {
      return el;
    }
    el = el.parentElement;
  }
  return null;
};
```

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

---

### 5. Write a function to find the corresponding node in two identical DOM trees

- Given two same DOM tree A, B, and an Element a in A, find the corresponding Element b in B. By corresponding, we mean a and b have the same relative position to their DOM tree root.

```js copy
const A = document.createElement("div");
A.innerHTML = `
<div>
<div>
  <div>
    <div id="node1"></div>
  </div>
  <div>
  </div>
  <div>
    <div>
      <p id="node2"></p>
    </div>
  </div>
<div>
</div>`;

const B = A.cloneNode(true);
const node1 = A.querySelector("#node1");
const node2 = A.querySelector("#node2");
const node1Target = B.querySelector("#node1");
const node2Target = B.querySelector("#node2");

findCorrespondingNode(A, B, node1); // node1Target
findCorrespondingNode(A, B, node2); // node2Target
```

```js copy
const findCorrespondingNode = (rootA, rootB, target) => {
  if (rootA === target) return rootB;

  if (rootA.childElementCount) {
    for (let i = 0; i < rootA.childElementCount; i++) {
      let result = findCorrespondingNode(
        rootA.children[i],
        rootB.children[i],
        target
      );
      if (result) {
        return result;
      }
    }
  }
};
```

**References**

- https://bigfrontend.dev/problem/find-corresponding-node-in-two-identical-DOM-tree

---

### 6. Write a function to get depth of a given DOM tree

- A depth of a given DOM tree is the max depth till which DOM nodes are nested

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {number}
 */
function getHeight(root) {
  if (!root) return 0;

  let maxDepth = 0;

  const helper = (current, depth = 1) => {
    if (current.hasChildNodes()) {
      for (let child of current.children) {
        helper(child, depth + 1);
      }
    }
    maxDepth = Math.max(maxDepth, depth);
  };

  helper(root);
  return maxDepth;
}
```

---

### 7. Implement a function to get the root node of a given DOM fragment (document.getRootNode() method)

- Root node is the topmost parent node of any given DOM fragment

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {HTMLElement | null}
 */
function getRootNode(tree) {
  if (!tree) return null;

  while (tree.parentElement) {
    tree = tree.parentElement;
  }

  return tree;
}
```

**References**

- https://javascript.info/dom-navigation

---

### 8. Implement a function to get unique tag names in a given DOM tree

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {Array}
 */
function getUniqueTags(root, result = new Set()) {
  if (!root) return [];

  if (!result.has(root.tagName)) {
    result.add(root.tagName);
  }

  if (root.hasChildNodes()) {
    for (let child of root.children) {
      getUniqueTags(child, result);
    }
  }

  return [...result];
}
```

**References**

- https://bigfrontend.dev/problem/get-DOM-tags

---

### 9. Implement a function to get elements by tag name (document.getElementsByTagName() method)

- The getElementsByTagName method of Document interface returns an HTMLCollection of elements with the given tag name.
- For example, document.getElementsByTagName('div') returns a collection of all div elements in the document.

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {Array}
 */
function getElementsByTagName(root, tagName) {
  if (!root) return [];

  let result = [];

  if (root.tagName.toLowerCase() === tagName.toLowerCase()) {
    result.push(root);
  }

  if (root.hasChildNodes()) {
    for (let child of root.children) {
      result = result.concat(getElementsByTagName(child, tagName));
    }
  }

  return result;
}
```

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByTagName

---

### 10. Implement a function to check if a given DOM tree has duplicate IDs

- In a given DOM tree, the id on each node has be unique
- Although HTML is very forgiving, but we should avoid duplicate identifiers

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {Boolean}
 */
function hasDuplicateId(tree, idSet = new Set()) {
  if (!tree) return false;

  if (idSet.has(tree.id)) return true;

  tree.id && idSet.add(tree.id);

  if (tree.hasChildNodes()) {
    for (let child of tree.children) {
      const result = hasDuplicateId(child, idSet);
      if (result) return true;
    }
  }

  return false;
}
```

### 11. Implement a function to get all descendants of a given DOM node

- This function retrieves all child elements, regardless of depth.

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {Array}
 */
function getAllDescendants(root) {
  if (!root) return [];

  let descendants = [];

  const helper = (node) => {
    if (node) {
      descendants.push(node);
      if (node.children.length) {
        for (let child of node.children) {
          helper(child);
        }
      }
    }
  };

  helper(root);
  return descendants;
}
```
**References**

- https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children

---

### 12. Implement a function to clone a DOM tree

- This function creates a deep copy of the given DOM node and its descendants.

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {HTMLElement | null}
 */
function cloneDOMTree(root) {
  return root ? root.cloneNode(true) : null;
}

```
**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode

---

### 13. Implement a function to count all elements in a DOM tree

- This function counts all child elements, regardless of depth.

```js copy
/**
 * Counts all child elements in a DOM tree.
 *
 * @param {HTMLElement | null} root - The root element from which to start counting.
 * @return {number} - The total number of child elements.
 */
function countElements(root) {
  if (!root) return 0;

  let count = 1; 

  for (let child of root.children) {
    count += countElements(child);
  }
  return count;
}
```
---
### 14. Implement a function to serialize a DOM tree into a string

- This function converts a DOM tree into a string representation.

```js copy
/**
 * @param {HTMLElement | null} tree
 * @return {string}
 */
function serializeDOM(root) {
  if (!root) return '';

  let str = `<${root.tagName.toLowerCase()}`;

  for (let attr of root.attributes) {
    str += ` ${attr.name}="${attr.value}"`;
  }

  str += '>';

  if (root.hasChildNodes()) {
    for (let child of root.children) {
      str += serializeDOM(child);
    }
  }

  str += `</${root.tagName.toLowerCase()}>`;
  return str;
}
```
**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes

---
### 15. What is event delegation in the DOM and how can it be implemented in JavaScript?
- Event delegation allows you to attach a single event listener to a parent element to manage events from its child elements, leveraging event bubbling. This improves performance and simplifies handling dynamically added elements.

Here’s an example of how event delegation works:
```js copy
document.querySelector('#parent').addEventListener('click', function(event) {
  if (event.target && event.target.matches('button')) {
    console.log('Button clicked:', event.target.textContent);
  }
});
```

This approach optimizes performance and reduces memory usage, as fewer event listeners are attached to individual elements.

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes
---

### 16. How can you find all leaf nodes in a given DOM tree?
- Leaf nodes in a DOM tree are elements that do not have any child elements. You can traverse the tree recursively to collect all leaf nodes.

Implementation:
```js copy
function getLeafNodes(element) {
  let leafNodes = [];

  // Base case: if the element has no children, it's a leaf node
  if (!element.children || element.children.length === 0) {
    leafNodes.push(element);
    return leafNodes;
  }

  // Recursive case: traverse through each child
  for (let i = 0; i < element.children.length; i++) {
    leafNodes = leafNodes.concat(getLeafNodes(element.children[i]));
  }

  return leafNodes;
}

// Example usage:
const leafNodes = getLeafNodes(document.body);
console.log('Leaf nodes:', leafNodes);

```

In this implementation:

The function checks if the current element has no children. If so, it adds it to the leafNodes array.
Otherwise, it recursively collects leaf nodes from each child.

**References**

- https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children
---


